<!DOCTYPE html>
<html lang="en">
<head>
    <title>Microsoft Cognitive Services Speech SDK JavaScript Quickstart for Multi-Device Conversation (Preview)</title>
    <meta content="Microsoft Cognitive Services Speech SDK JavaScript Quickstart for Multi-Device Conversation (Preview)"
          name="Description">
    <meta charset="utf-8">
    <meta content="width=device-width, initial-scale=1"
          name="viewport">
    <style>
        body {
            font-family: 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif;
            font-size: 14px;
        }

        table, th, td {
            border: 1px solid #f1f1f1;
            border-collapse: collapse;
        }

        th, td {
            padding: 10px;
        }

        .mode {
            font-size: 18px;
        }

        input:not(disabled) {
            font-weight: bold;
            color: black;
        }

        button {
            padding: 4px 8px;
            background: #0078d4;
            color: #ffffff;
        }

            button:disabled {
                padding: 4px 8px;
                background: #ccc;
                color: #666;
            }

        input[type=radio] {
            position: relative;
            z-index: 1;
        }

            input[type=radio] + label {
                padding: 8px 4px 8px 30px;
                margin-left: -30px;
            }

            input[type=radio]:checked + label {
                background: #0078d4;
                color: #ffffff;
            }
    </style>
</head>
<body>
    <div id="warning">
        <h1 style="font-weight:500;">Speech Recognition Speech SDK not found (microsoft.cognitiveservices.speech.sdk.bundle.js missing).</h1>
    </div>
    <div id="content"
         style="display:none">
        <table width="100%">
            <tr>
                <td></td>
                <td>
                    <h1 style="font-weight:500;">Microsoft Cognitive Services Speech SDK JavaScript Quickstart for Multi-Device Conversation (Preview)</h1>
                </td>
            </tr>
            <tr>
                <td align="right">I want to</td>
                <td class="mode">
                    <input checked
                           id="startMode"
                           name="mode"
                           type="radio"
                           value="start"> <label for="startMode">create a conversation</label>
                    <input id="joinMode"
                           name="mode"
                           type="radio"
                           value="join"> <label for="joinMode">join a conversation</label>
                </td>
            </tr>
            <tr>
                <td align="right">
                    <label for="subscriptionKey">
                        <a href="https://docs.microsoft.com/azure/cognitive-services/speech-service/get-started"
                           rel="noreferrer noopener"
                           target="_blank">Subscription Key</a>
                    </label>
                </td>
                <td>
                    <input id="subscriptionKey"
                           placeholder="subscription key"
                           size="40"
                           type="text"
                           value="">
                </td>
            </tr>
            <tr>
                <td align="right"><label for="serviceRegion">Region</label></td>
                <td>
                    <input id="serviceRegion"
                           placeholder="region"
                           size="40"
                           type="text"
                           value="westus">
                </td>
            </tr>
            <tr>
                <td align="right"><label for="conversationId">Conversation code</label></td>
                <td>
                    <input disabled
                           id="conversationId"
                           maxlength="5"
                           placeholder="5 character code"
                           size="40"
                           type="text">
                </td>
            </tr>
            <tr>
                <td align="right"><label for="displayName">Display name</label></td>
                <td>
                    <input id="displayName"
                           maxlength="50"
                           placeholder="Host"
                           size="40"
                           type="text"
                           value="Host">
                </td>
            </tr>
            <tr>
                <td align="right">
                    <label for="languageCode">
                        <a href="https://docs.microsoft.com/azure/cognitive-services/speech-service/multi-device-conversation#language-support"
                           rel="noreferrer noopener"
                           target="_blank">Language code</a>
                    </label>
                </td>
                <td>
                    <input id="languageCode"
                           maxlength="6"
                           placeholder="en-US"
                           size="40"
                           type="text"
                           value="en-US">
                </td>
            </tr>
            <tr>
                <td></td>
                <td>
                    <button id="startConversation">Create conversation</button> <button disabled
                                                                                       id="joinConversation">
                        Join conversation
                    </button> <button disabled
                                      id="leaveConversation">
                        Leave conversation
                    </button>
                </td>
            </tr>
            <tr>
                <td></td>
                <td></td>
            </tr>
            <tr>
                <td align="right"><label for="message">Interact</label></td>
                <td>
                    <input disabled
                           id="message"
                           placeholder="type something"
                           type="text"> <button disabled
                                                id="sendMessage">
                        Send instant message
                    </button> <button disabled
                                      id="startSpeaking"
                                      style="margin-left:50px">
                        Start speaking
                    </button> <button disabled
                                      id="stopSpeaking">
                        Stop speaking
                    </button>
                </td>
            </tr>
            <tr>
                <td align="right"
                    valign="top">
                    <label for="phraseDiv">Incoming messages</label>
                </td>
                <td>
                    <textarea id="phraseDiv"
                              style="display: inline-block;width:500px;height:200px"></textarea>
                </td>
            </tr>
        </table>
    </div>
    
    <!-- Speech SDK reference sdk. -->
    <script src="https://aka.ms/csspeech/jsbrowserpackageraw"></script>
    
    <!-- Speech SDK USAGE -->
    <script>
        // status fields and start button in UI
        var phraseDiv;

        // subscription key and region for speech services.
        var subscriptionKey, serviceRegion;
        var SpeechSDK;
        var conversation;
        var conversationTranslator;

        var startModeButton, joinModeButton;
        var startConversationButton, joinConversationButton, leaveConversationButton;
        var conversationId, displayName, languageCode;
        var message, sendIM, startSpeaking, stopSpeaking;

        var isHost = true;
        var isDisconnecting = false;

        document.addEventListener("DOMContentLoaded", function () {
            subscriptionKey = document.getElementById("subscriptionKey");
            serviceRegion = document.getElementById("serviceRegion");
            phraseDiv = document.getElementById("phraseDiv");
            startModeButton = document.getElementById("startMode");
            joinModeButton = document.getElementById("joinMode");
            conversationId = document.getElementById("conversationId");
            displayName = document.getElementById("displayName");
            languageCode = document.getElementById("languageCode");
            startConversationButton = document.getElementById("startConversation");
            joinConversationButton = document.getElementById("joinConversation");
            leaveConversationButton = document.getElementById("leaveConversation");
            messageInputBox = document.getElementById("message");
            sendMessageButton = document.getElementById("sendMessage");
            startSpeakingButton = document.getElementById("startSpeaking");
            stopSpeakingButton = document.getElementById("stopSpeaking");

            // check for a saved subscription key
            if (window.localStorage.getItem("demoSubscriptionKey")) {
                subscriptionKey.value = window.localStorage.getItem("demoSubscriptionKey");
            }

            // show the UI elements required for starting a conversation
            startModeButton.addEventListener("click", function () {
                conversationId.disabled = true;
                startConversationButton.disabled = false;
                joinConversationButton.disabled = true;
                subscriptionKey.disabled = false;
                serviceRegion.disabled = false;
                isHost = true;
                displayName.value = "Host";
            });

            // show the UI elements required for joining a conversation
            joinModeButton.addEventListener("click", function () {
                conversationId.disabled = false;
                startConversationButton.disabled = true;
                joinConversationButton.disabled = false;
                subscriptionKey.disabled = true;
                serviceRegion.disabled = true;
                isHost = false;
                displayName.value = "Participant";
            });

            // send a text message when there is an active conversation available
            sendMessageButton.addEventListener("click", function () {
                var textToSend = messageInputBox.value;
                if (textToSend == "") {
                    alert("Enter some text");
                    return;
                }
                conversationTranslator.sendTextMessageAsync(textToSend, function () {
                    console.log("send text message");
                    phraseDiv.innerHTML += "\rsend text message";
                }, function (error) {
                    console.log("error sending text message " + error);
                    phraseDiv.innerHTML += "\r" + error;
                });
            });

            // start speaking (the language must be a speech supported language)
            // the SDK will attempt to turn your microphone on in order to capture your voice
            // a prompt may appear requesting that you grant browser microphone permissions
            startSpeakingButton.addEventListener("click", function () {
                conversationTranslator.startTranscribingAsync(function () {
                    console.log("send start speak");
                    phraseDiv.innerHTML += "\rstart speaking";
                    startSpeakingButton.disabled = true;
                    stopSpeakingButton.disabled = false;
                }, function (error) {
                    console.log("error speaking " + error);
                    phraseDiv.innerHTML += "\r" + error;
                });
            });

            // stop speaking
            stopSpeakingButton.addEventListener("click", function () {
                conversationTranslator.stopTranscribingAsync(function () {
                    console.log("send stop speak");
                    phraseDiv.innerHTML += "\rstop speaking";
                    startSpeakingButton.disabled = false;
                    stopSpeakingButton.disabled = true;
                }, function (error) {
                    console.log("error stop speaking " + error);
                    phraseDiv.innerHTML += "\r" + error;
                });
            });

            // start a conversation as the host
            startConversationButton.addEventListener("click", function () {

                reset();

                window.localStorage.setItem("demoSubscriptionKey", subscriptionKey.value);
                startConversationButton.disabled = true;

                if (serviceRegion.value === "" || serviceRegion.value === "region") {
                    alert("Please enter your Microsoft Cognitive Services Speech region.");
                    resetUI();
                    return;
                }
                if (subscriptionKey.value === "" || subscriptionKey.value === "subscription") {
                    alert("Please enter your Microsoft Cognitive Services Speech subscription key.");
                    resetUI();
                    return;
                }
                
                var language = languageCode.value;
                if (language == "") {
                    language = "en-US";
                }

                var nickname = displayName.value;
                if (nickname == "") {
                    nickname = "Host";
                }

                if (conversation) {
                    conversation.dispose();
                    conversation = undefined;
                }

                var config = SpeechSDK.SpeechTranslationConfig.fromSubscription(subscriptionKey.value, serviceRegion.value);
                config.speechRecognitionLanguage = language;
                phraseDiv.innerHTML = "Starting a conversation...";

                // create a conversation
                conversation = SpeechSDK.Conversation.createConversationAsync(config, function () {

                    // success callback for create conversation
                    conversation.startConversationAsync(
                        // success callback for start conversation
                        function (result) {
                            conversationId.value = conversation.conversationId;
                            phraseDiv.innerHTML += "\rStarted conversation " + conversation.conversationId;
                            leaveConversationButton.disabled = false;

                            // join the conversation as the host
                            handleJoinConversation(true, conversation, nickname, language,

                                // success callback for join conversation
                                function (result) {
                                    // joined ok
                                    phraseDiv.innerHTML += "\rJoined conversation " + conversation.conversationId;
                                    sendMessageButton.disabled = false;
                                    startSpeakingButton.disabled = false;
                                    messageInputBox.disabled = false;

                                },

                                // error callback for join conversation
                                function (error) {
                                    // error on joining
                                    console.log(error);
                                    phraseDiv.innerHTML += "\rError joining conversation: " + error;
                                    resetUI();
                                });
                        },

                        // error callback for start conversation
                        function (error) {
                            console.log(error);
                            phraseDiv.innerHTML += "\rError starting conversation: " + error;
                            resetUI();
                        });

                    },

                    // error callback for create conversation
                    function (error) {
                        console.log(error);
                        phraseDiv.innerHTML += "\rError creating conversation: " + error;
                        resetUI();
                    });
            });

            // join a conversation as a participant
            joinConversationButton.addEventListener("click", function () {

                reset();

                window.localStorage.setItem("demoSubscriptionKey", subscriptionKey.value);
                joinConversationButton.disabled = true;
                phraseDiv.innerHTML = "joining a conversation";
                leaveConversationButton.disabled = false;

                var conversationCode = conversationId.value;
                if (conversationCode === "" || conversationCode.length !== 5) {
                    alert("Please enter a valid conversation id.");
                    resetUI();
                    return;
                }

                var language = languageCode.value;
                if (language == "") {
                    language = "en-US";
                }

                var nickname = displayName.value;
                if (nickname == "") {
                    nickname = "participant";
                }

                // join the conversation as a participant
                handleJoinConversation(false, conversationCode, nickname, language,

                    // success callback for join conversation
                    function (result) {
                        phraseDiv.innerHTML += "\rJoined conversation " + conversationCode;
                        sendMessageButton.disabled = false;
                        startSpeakingButton.disabled = false;
                        messageInputBox.disabled = false;

                    },

                    // error callback for join conversation
                    function (error) {
                        console.log(error);
                        phraseDiv.innerHTML += "\rError joining conversation: " + error;
                        resetUI();
                    });

            });

            // leave a conversation
            leaveConversationButton.addEventListener("click", function () {

                var leaveConversation = false;
                if (isHost) {
                    if (confirm("Leaving the conversation will end this conversation for all participants.")) {
                        if (typeof conversationTranslator !== "undefined") {
                            leaveConversation = true;
                        }
                    }
                } else {
                    if (typeof conversationTranslator !== "undefined") {
                        leaveConversation = true;
                    }
                }

                if (leaveConversation) {
                    handleLeaveConversation();                    
                }
            });

            // shared function for a host or participant to connect a conversation translator to a conversation
            function handleJoinConversation(isHost, conversation, nickname, language, cb, err) {

                var participants = [];

                // optionally, create the audio config
                var audioConfig = SpeechSDK.AudioConfig.fromDefaultMicrophoneInput();

                // create the conversation translator
                conversationTranslator = new SpeechSDK.ConversationTranslator(audioConfig);

                // hook up the events

                // the conversation is cancelled
                conversationTranslator.canceled = (s, e) => {
                    window.console.log(e);
                    switch (e.errorCode) {
                        case SpeechSDK.CancellationErrorCode.NoError:
                            // the user has been disconnected
                            phraseDiv.innerHTML += "\r" + e.errorDetails;
                            break;
                        default: 
                            phraseDiv.innerHTML += "\rCanceled due to error. " + e.errorCode + ": " + e.errorDetails;   
                            break;
                    }

                    // if it was unexpected, make sure leave is called
                    if (!isDisconnecting) {
                        handleLeaveConversation();
                    }
                };

                // info alert for the conversation about to expire
                conversationTranslator.conversationExpiration = (s, e) => {
                    window.console.log(e);
                    phraseDiv.innerHTML += "\rConversation will expire in " + e.expirationTime + " minutes";
                };

                // info alert that participants have joined or left or a change has been made
                conversationTranslator.participantsChanged = (s, e) => {
                    window.console.log(e);
                    phraseDiv.innerHTML += "\rThe following participant(s) have ";
                    switch (e.reason) {
                        case SpeechSDK.ParticipantChangedReason.JoinedConversation:
                            phraseDiv.innerHTML += "joined";
                            for (var i =0; i< e.participants.length; i++) {
                                participants.push(e.participants[i]);
                            }
                            break;

                        case SpeechSDK.ParticipantChangedReason.LeftConversation:
                            phraseDiv.innerHTML += "left";
                            break;

                        case SpeechSDK.ParticipantChangedReason.Updated:
                            phraseDiv.innerHTML += "been updated";
                            break;
                    }
                    phraseDiv.innerHTML += ":";

                    for (var i = 0; i < e.participants.length; i++) {
                        phraseDiv.innerHTML += "\t" + e.participants[i].displayName;
                    }

                }

                // session has started
                conversationTranslator.sessionStarted = (s, e) => {
                    window.console.log(e);
                    phraseDiv.innerHTML += "\rSession started: " + e.sessionId;
                };

                // session has stopped
                conversationTranslator.sessionStopped = (s, e) => {
                    window.console.log(e);
                    phraseDiv.innerHTML += "\rSession stopped: " + e.sessionId;
                };

                // instant message has been received
                conversationTranslator.textMessageReceived = (s, e) => {
                    window.console.log(e);
                    phraseDiv.innerHTML += "\rReceived an instant message from " + getParticipantName(e.result.participantId) + ": " + e.result.text;
                    phraseDiv.innerHTML += "\rTranslated into " + language + ": " + e.result.translations.get(language);
                };

                // a 'partial' message has been received
                conversationTranslator.transcribed = (s, e) => {
                    window.console.log(e);
                    phraseDiv.innerHTML += "\rReceived a transcription from " + getParticipantName(e.result.participantId) + ": " + e.result.text;
                    phraseDiv.innerHTML += "\rTranslated into " + language + ": " + e.result.translations.get(language);
                };

                // a 'final' message has been received
                conversationTranslator.transcribing = (s, e) => {
                    window.console.log(e);
                    phraseDiv.innerHTML += "\rReceived a partial transcription from " + getParticipantName(e.result.participantId) + ": " + e.result.text;
                    phraseDiv.innerHTML += "\rTranslated into " + language + ": " + e.result.translations.get(language);
                };

                try {
                    if (isHost) {
                        // join the conversation as the host
                        conversationTranslator.joinConversationAsync(conversation, nickname, cb, err);
                    } else {
                        // join the conversation as a participant
                        conversationTranslator.joinConversationAsync(conversation, nickname, language, cb, err);
                    }
                } catch (e) {
                    window.console.log(e);
                    phraseDiv.innerHTML += "\rUnexpected error joining: " + e;
                    if (err) {
                        err(e);
                    }
                }

                function getParticipantName(id) {
                    for (var i = 0; i < participants.length; i++) {
                        if (participants[i].id == id) {
                            return participants[i].displayName;
                        }
                    }
                    return id;
                }
            }

            function handleLeaveConversation() {
                isDisconnecting = true;
                if (isHost) {
                    conversation.endConversationAsync(

                        // success callback for end conversation
                        function () {
                            phraseDiv.innerHTML += "\rEnded conversation.";

                            // check the conversation object is still available
                            if (conversation === undefined) { 
                                reset();
                                resetUI();
                                return; 
                            }
                            conversation.deleteConversationAsync(

                                // success callback for delete conversation
                                function (result) {
                                    phraseDiv.innerHTML += "\rLeft conversation.";
                                    reset();
                                    resetUI();
                                },

                                // error callback for delete conversation
                                function (error) {
                                    console.log(error);
                                    phraseDiv.innerHTML += "\rError leaving conversation: " + error;
                                    reset();
                                    resetUI();
                                }
                            );
                        },

                        // error callback for end conversation
                        function (error) {
                            console.log(error);
                            phraseDiv.innerHTML += "\rError ending conversation: " + error;
                            reset();
                            resetUI();
                        });
                } else {
                    conversationTranslator.leaveConversationAsync(

                        // success callback for leave conversation
                        function (result) {
                            phraseDiv.innerHTML += "\rLeft conversation.";
                            reset();
                            resetUI();
                        },

                        // error callback for leave conversation
                        function (error) {
                            console.log(error);
                            phraseDiv.innerHTML += "\rError leaving conversation: " + error;
                            reset();
                            resetUI();
                        }
                    );
                }
            }

            // helper function to dispose the conversation and conversation translator objects
            function reset() {

                if (conversation) {
                    try {
                        conversation.dispose();
                    } catch (e) {}
                    conversation = undefined;
                }

                if (conversationTranslator) {
                    try {
                        conversationTranslator.dispose();
                    } catch (e) {}
                    conversationTranslator = undefined;
                }

                isDisconnecting = false;
            }

            // helper function to reset the form UI elements to default values
            function resetUI() {

                sendMessageButton.disabled = true;
                startSpeakingButton.disabled = true;
                stopSpeakingButton.disabled = true;
                messageInputBox.disabled = true;
                leaveConversationButton.disabled = true;

                joinModeButton.disabled = false;
                startModeButton.disabled = false;

                if (isHost) {
                    startConversationButton.disabled = false;
                    joinConversationButton.disabled = true;
                } else {
                    startConversationButton.disabled = true;
                    joinConversationButton.disabled = false;
                }
            }

            // hook up to the sdk
            if (!!window.SpeechSDK) {
                SpeechSDK = window.SpeechSDK;

                document.getElementById('content').style.display = 'block';
                document.getElementById('warning').style.display = 'none';
            }
        });
    </script>
</body>
</html>
